# -*- coding: utf-8 -*-
"""
Module Description:
    python 中的异常栈跟踪,
    traceback.print_exc方法，是将异常栈里的信息一条一条记录到traceback._print中
    在记录到kibana,导致一条完整的异常链，在Kibana中不是在一条记录中显示，导致日志查看不方便
    所以重新定义print_exc方法，将异常链信息整理之后，在一次写入日志

Date: 2017/4/12
Author:
"""
import traceback
import sys


def _return_tb_message(tb, limit=None):
    """Print up to 'limit' stack trace entries from the traceback 'tb'.

    If 'limit' is omitted or None, all entries are printed.  If 'file'
    is omitted or None, the output goes to sys.stderr; otherwise
    'file' should be an open file or file-like object with a write()
    method.
    """

    if limit is None:
        if hasattr(sys, 'tracebacklimit'):
            limit = sys.tracebacklimit
    n = 0
    message = ''
    while tb is not None and (limit is None or n < limit):
        f = tb.tb_frame
        lineno = tb.tb_lineno
        co = f.f_code
        filename = co.co_filename
        name = co.co_name
        message += '  File "%s", line %d, in %s' % (filename, lineno, name) + '\n'

        traceback.linecache.checkcache(filename)
        line = traceback.linecache.getline(filename, lineno, f.f_globals)
        if line:
            message += '    ' + line.strip() + '\n'
        tb = tb.tb_next
        n = n + 1
    return message


def print_exception(etype, value, tb, limit=None, file=None, request_args=None, log_level='error', extra=None):
    """Print exception up to 'limit' stack trace entries from 'tb' to 'file'.

    This differs from print_tb() in the following ways: (1) if
    traceback is not None, it prints a header "Traceback (most recent
    call last):"; (2) it prints the exception type and value after the
    stack trace; (3) if type is SyntaxError and value has the
    appropriate format, it prints the line where the syntax error
    occurred with a caret on the next line indicating the approximate
    position of the error.
    """
    error_str = ''
    if file is None:
        file = sys.stderr
    if tb:
        error_str = 'Traceback (most recent call last): \n'
        error_str += _return_tb_message(tb, limit=limit)

    lines = traceback.format_exception_only(etype, value)

    for line in lines:
        error_str += line + '\n'
    if error_str:
        from framework.firefly_dc import runtime_logger as log
        # 如果传入了请求参数，表示异常日志中需要打印请求参数

        # 此处逻辑，转移至runtime_logger
        # error_str += 'request_args: {}'.format(str(request_args) + '\n') if request_args else ''

        log_opt = getattr(log, log_level)
        extra = extra if extra else {}
        log_opt(error_str, extra=extra)

        return error_str


def print_exc(exception_object=None, limit=None, file=None, request_args=None, log_level='error', extra=None):
    """Shorthand for 'print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)'.
    (In fact, it uses sys.exc_info() to retrieve the same information
    in a thread-safe way.)
    :params exception_object：Exception对象,增加该参数是因为外部使用时都传了Exception对象，虽然实际没有使用
    :params log_level: 控制日志输出级别，默认error
    """
    if file is None:
        file = sys.stderr
    try:
        etype, value, tb = sys.exc_info()
        return print_exception(etype, value, tb, limit, file, request_args, log_level=log_level, extra=extra)
    finally:
        etype = value = tb = None
    return  ''